package com.gitee.wsl.mathematics.vector.vec2

import com.gitee.wsl.ext.array.FloatArray
import com.gitee.wsl.io.dataproxy.DataGet
import com.gitee.wsl.io.dataproxy.asProxy
import com.gitee.wsl.mathematics.coordinate.d2.Coordinate2F
import com.gitee.wsl.mathematics.vector.vec3.Vec3f
import com.gitee.wsl.mathematics.vector.vec4.Vec4f
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt

/**
 * A class representing a two-dimensional / planar vector.
 */
open class Vec2f(override val proxy: DataGet<Float>):
    Coordinate2F<Vec2f> {
    constructor(x: Float=0.0f, y: Float=0.0f):this(FloatArray(x,y).asProxy())
    constructor(a: Number) : this(a.toDouble(), a.toDouble())
    constructor(x: Number, y: Number) : this(x.toFloat(), y.toFloat())
    constructor(base: Vector2<*,*>):this(base.x, base.y)

    override val self
        get() = this

    override val vec2 get() = this
    override val vec3 get() = Vec3f(x, y, 0.0)
    override val vec4 get() = Vec4f(x, y, 0.0, 0.0)

    override fun create(x: Float, y: Float): Vec2f = Vec2f(x,y)

    companion object {
        val INFINITY get() = positiveInfinity
        val right get()     = Vec2f(1.0, 0.0)
        val left get()      = Vec2f(-1.0, 0.0)
        val up get()        = Vec2f(0.0, 1.0)
        val down get()      = Vec2f(0.0, -1.0)
        val ONE get()       = Vec2f(1.0, 1.0)
        val ZERO get()      = Vec2f(0.0, 0.0)
        val negativeInfinity get() = Vec2f(Double.NEGATIVE_INFINITY)
        val positiveInfinity get() = Vec2f(Double.POSITIVE_INFINITY)

        val ORIGIN = Vec2f(0.0, 0.0)
        val UNIT_X = Vec2f(1.0, 0.0)
        val UNIT_Y = Vec2f(0.0, 1.0)

        /**
         * This class has all internal methods, used by Polygon, Morph, etc.
         */

        internal fun distance(x: Float, y: Float) = sqrt(x * x + y * y)

        /**
         * Returns unit vector representing the direction to this point from (0, 0)
         */
        fun directionVector(x: Float, y: Float): Vec2f {
            val d = distance(x, y)
            require(d > 0f) { "Required distance greater than zero" }
            return Vec2f(x / d, y / d)
        }

        fun directionVector(angleRadians: Float) = Vec2f(cos(angleRadians), sin(angleRadians))
    }

    override fun toString(): String {
        val xInt = x.toInt();
        val yInt = y.toInt()
        val xs = if (x == xInt.toFloat()) xInt.toString() else x.toString()
        val ys = if (y == yInt.toFloat()) yInt.toString() else y.toString()

        return "[$xs, $ys]"
    }

    override fun equals(other: Any?) = other is Vec2f && x == other.x && y == other.y
    override fun hashCode() = 31 * x.hashCode() + y.hashCode()

 }


/**
 * A class representing a two-dimensional / planar vector.
 */
/*
open class Vec2f2(x: Float, y: Float, override val values: FloatArray = FloatArray(x,y)):
     Vector2f<Vec2f>, Coordinate2F<Vec2f> {
    constructor() : this(0.0, 0.0)
    constructor(a: Number) : this(a.toDouble(), a.toDouble())
    constructor(x: Number, y: Number) : this(x.toFloat(), y.toFloat())

    //override  val values: DoubleArray = DoubleArray(2)

   */
/* open val x
        get() = this[0]

    open val y
        get() = this[1]

    override val length
        get() = sqrt(sqrLength)
        
    override val sqrLength get() = x * x + y * y

    override val normalized: Vec2f get() = this / length
    override val abs: Vec2f get() = Vec2f(abs(x), abs(y))
    override val sign: Vec2f get() = Vec2f(sign(x), sign(y))
    override val ceil: Vec2f get() = Vec2f(ceil(x), ceil(y))
    override val floor: Vec2f get() = Vec2f(floor(x), floor(y))
    override val round: Vec2f get() = Vec2f(round(x), round(y))
    open val perpendicular: Vec2f get() = Vec2f(-y, x)*//*

    override val vec2 get() = this
    override val vec3 get() = Vec3f(x, y, 0.0)
    override val vec4 get() = Vec4f(x, y, 0.0, 0.0)

    override fun create(x: Float, y: Float): Vec2f = Vec2f(x,y)

    companion object {
        val right get()     = Vec2f(1.0, 0.0)
        val left get()      = Vec2f(-1.0, 0.0)
        val up get()        = Vec2f(0.0, 1.0)
        val down get()      = Vec2f(0.0, -1.0)
        val one get()       = Vec2f(1.0, 1.0)
        val zero get()      = Vec2f(0.0, 0.0)
        val negativeInfinity get() = Vec2f(Double.NEGATIVE_INFINITY)
        val positiveInfinity get() = Vec2f(Double.POSITIVE_INFINITY)
    }

    override operator fun plus(other: Vec2f) = Vec2f(x + other.x, y + other.y)
    open operator fun plus(other: Vec3f) = Vec3f(x + other.x, y + other.y, other.z)
    open operator fun plus(other: Vec4f) = Vec4f(x + other.x, y + other.y, other.z, other.w)

    override operator fun minus(other: Vec2f) = Vec2f(x - other.x, y - other.y)
    open operator fun minus(other: Vec3f) = Vec3f(x - other.x, y - other.y, -other.z)
    open operator fun minus(other: Vec4f) = Vec4f(x - other.x, y - other.y, -other.z, -other.w)

    override operator fun times(a: Number) = Vec2f(x * a.toDouble(), y * a.toDouble())
    override operator fun times(other: Vec2f) = Vec2f(x * other.x, y * other.y)
    open operator fun times(other: Vec3f) = Vec3f(x * other.x, y * other.y, 0.0)
    open operator fun times(other: Vec4f) = Vec4f(x * other.x, y * other.y, 0.0, 0.0)
    open operator fun times(rotation: Quaternion) = rotation * vec3

    override operator fun div(a: Number) = Vec2f(x / a.toDouble(), y / a.toDouble())
    override operator fun div(other: Vec2f) = Vec2f(x / other.x, y / other.y)
    open operator fun div(other: Vec3f) = Vec3f(x / other.x, y / other.y, Double.POSITIVE_INFINITY)
    open operator fun div(other: Vec4f) = Vec4f(x / other.x, y / other.y, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)
    override fun divBy(a: Number): Vec2f = Vec2f(a.toDouble() / x, a.toDouble() / y)


    override operator fun rem(a: Number) = Vec2f(x % a.toDouble(), y % a.toDouble())
    override operator fun rem(other: Vec2f) = Vec2f(x % other.x, y % other.y)
    open operator fun rem(other: Vec3f) = Vec3f(x % other.x, y % other.y, Double.NaN)
    open operator fun rem(other: Vec4f) = Vec4f(x % other.x, y % other.y, Double.NaN, Double.NaN)
    override fun remBy(a: Number): Vec2f = Vec2f(a.toDouble() % x, a.toDouble() % y)


    //override operator fun compareTo(other: Vec2f) = length.compareTo(other.length)
    //operator fun compareTo(other: Vec3f) = length.compareTo(other.length)
    //operator fun compareTo(other: Vec4f) = length.compareTo(other.length)

    //override operator fun unaryMinus() = Vec2f(-x, -y)

    */
/**The distance from this vector to [other].*//*

    override infix fun distanceTo(other: Vec2f) = (other - this).length


    */
/**The distance from this vector to [other].*//*

    infix fun distanceTo(other: Vec3f) = (other - this).length

    */
/**The distance from this vector to [other].*//*

    infix fun distanceTo(other: Vec4f) = (other - this).length

    */
/**The normal direction from this vector to [other].*//*

    override infix fun directionTo(other: Vec2f) = (other - this).normalized

    */
/**The normal direction from this vector to [other].*//*

    open infix fun directionTo(other: Vec3f) = (other - this).normalized

    */
/**The normal direction from this vector to [other].*//*

    open infix fun directionTo(other: Vec4f) = (other - this).normalized

    */
/**
     * Dot product.
     * @return The dot product between this vector and [other].
     *//*

    override infix fun dot(other: Vec2f) = x * other.x + y * other.y

    */
/**
     * Dot product.
     * @return The dot product between this vector and [other].
     *//*

    //override infix fun dot(other: Vec3f) = x * other.x + y * other.y

    */
/**
     * Dot product.
     * @return The dot product between this vector and [other].
     *//*

    //override infix fun dot(other: Vec4f) = x * other.x + y * other.y

    */
/**
     * The angle between two vectors.
     * @return The angle between this vector and [other].
     *//*

    //override infix fun angleTo(other: Vec2f) = acos(dot(other) / (length * other.length))

    */
/**
     * The angle between two vectors.
     * @return The angle between this vector and [other].
     *//*

    //override infix fun angleTo(other: Vec3f) = other angleTo this

    override fun angleTo(other: Vector2<Float, *>): Float = acos(dot(other) / (length * other.length))

    override fun angleTo(other: Vector3<Float, *>): Float = other angleTo this

    */
/**
     * The angle between two vectors.
     * @return The angle between this vector and [other].
     *//*

//    infix fun angleTo(other: Vec4) = other angleTo this

    infix fun signedAngleTo(other: Vec2f) = angleTo(other) * sign(cross(other))

    override fun signedAngleTo(other: Vec3f, axis: Vec3f) = other.signedAngleTo(this, axis)

    */
/**
     * Cross product.
     * @return The cross product between this vector and [other].
     *//*

    infix fun cross(other: Vec2f) = x * other.x - y * other.y

    */
/**
     * Cross product.
     * @return The cross product between this vector and [other].
     *//*

    override infix fun cross(other: Vec3f) = other cross this

    */
/**The projection of this vector onto [other].*//*

    override infix fun projectOn(other: Vec2f) = other * dot(other) / other.sqrLength

    */
/**The projection of this vector onto [other].*//*

    override infix fun projectOn(other: Vec3f) = other * dot(other) / other.sqrLength

    */
/**The projection of this vector onto [other].*//*

    override infix fun projectOn(other: Vec4f) = other * dot(other) / other.sqrLength

    */
/**Reflects the vector from [normal].*//*

    override infix fun reflectFrom(normal: Vec2f): Vec2f {
        val norm = normal.normalized
        val factor = -2.0 * this dot norm
        return factor * norm + this
    }

    */
/**Reflects the vector from [normal].*//*

    infix fun reflectFrom(normal: Vec3f) = vec3 reflectFrom normal

    */
/**Reflects the vector from [normal].*//*

    infix fun reflectFrom(normal: Vec4f) = vec4 reflectFrom normal

    */
/**
     * Rotates the vector by [angle] radians
     * @param angle The angle in **radians**.
     * @return The vector rotated by [angle] radians.
     *//*

    open infix fun rotatedBy(angle: Double): Vec2f {
        val sin = sin(angle)
        val cos = cos(angle)
        return Vec2f((cos * x) - (sin * y), (sin * x) + (cos * y))
    }

    //operator fun component1() = x
    //operator fun component2() = y

    override fun toString(): String {
        val xInt = x.toInt();
        val yInt = y.toInt()
        val xs = if (x == xInt.toFloat()) xInt.toString() else x.toString()
        val ys = if (y == yInt.toFloat()) yInt.toString() else y.toString()

        return "[$xs, $ys]"
    }

    override fun equals(other: Any?) = other is Vec2f && x == other.x && y == other.y
    override fun hashCode() = 31 * x.hashCode() + y.hashCode()
}*/
